/**************************************************
|	File:		Player.cpp
|	Author:		Benjamin Budian
|	Course:		SGD
|	Purpose:	Player class handles all things player related
**************************************************/
#include "Player.h"
#include "CellAnimation.h"
#include "Game.h"
#include "Enemy.h"
#include "DestroyEntityMessage.h"
#include "MagicProjectile.h"
#include "SoundManager.h"
#include "Armor.h"
#include "Weapon.h"

#include "../SGD Wrappers/CSGD_Direct3D.h"
#include "../SGD Wrappers/CSGD_DirectInput.h"
#include "../SGD Wrappers/CSGD_MessageSystem.h"
#include "../SGD Wrappers//CSGD_EventSystem.h"
#include "../SGD Wrappers/CSGD_TextureManager.h"
#include "../SGD Wrappers/CSGD_XAudio2.h"

#include <cassert>

#define NUMCOLS 13
#define MOVEMENTCOLUMNS 9
#define BOWENDCOLUNM 13


CPlayer::CPlayer(void)
{
	PlayerAnimation = nullptr;
	m_eType = ENT_PLAYER;
	SetWidth(64);
	SetHeight(64);
	m_fMaxFireTimer = 0.6f;

	CSGD_EventSystem::GetInstance()->RegisterClient("ENEMY_KILLED", this);
	CSGD_EventSystem::GetInstance()->RegisterClient("DEBUG", this);

}


CPlayer::~CPlayer(void)
{
	delete PlayerAnimation;
	delete PlayerWeaponAnimation;

	for (int i = 0; i < NUM_ARMOR_PIECES; i++){
		delete ArmorAnimations[i];
	}


	CSGD_EventSystem::GetInstance()->UnregisterClientAll(this);
}

void CPlayer::Initialize(int nPlayerType)
{
	m_bDebug = false;

	//If player has selected a race of type 0 - to be implemented
	if (nPlayerType == 0){
		//Initialize PlayerAnimation for specific type
		PlayerAnimation = new CCellAnimation();
		PlayerAnimation->Initialize(64, 64, NUMCOLS, PLAYER_MOVE_DOWN, 9, false, _T("resource/images/Player/bb_tannedmale(resized).png"));

		//Initialize Health bar
		m_nHealthBarImgID = CSGD_TextureManager::GetInstance()->LoadTexture(_T("resource/images/Player/Health_Bar/bb_rad-rainbow-lifebar(resized).png"));

		//Set Attributes
		m_sArmor = 0;
		m_cArmor = 0;
		weapons = 0;
		m_sHealth = 100;
		m_fDamageMultiplier = 15;

		//Set Currently Equipped weapon
		m_eWeapon = BOW;  //MUST BE BEFORE WEAPON ANIMATION INIT
		//Set currently equipped weapon end animation cell
		m_eCurrentWeaponEndCell = BOW_CELLS; //MUST BE BEFORE WEAPON ANIMATION INIT

		//Initialize weapon animation - to be changed to only when weapon is picked up.
		PlayerWeaponAnimation = new CCellAnimation();
		PlayerWeaponAnimation->Initialize(128, 128, NUMCOLS, PLAYER_ATTACK_DOWN, BOWENDCOLUNM, false, _T("resource/images/Player/Weapons/bb_recurvebow_sheet(resized).png"));
		PlayerWeaponAnimation->SetAnimation(PLAYER_ATTACK_DOWN * m_eCurrentWeaponEndCell, m_eCurrentWeaponEndCell, m_eCurrentWeaponEndCell, true);

		//Initialize Armor Animations
		SetupArmor();

		//Set Current facing direction
		m_eMovementDirection = PLAYER_MOVE_DOWN;

		//Initializing Stats, timers and bools
		m_sKills = 0;
		m_fFireTimer = 0.0f;
		m_bGameOver = false;
		m_bInvincible = false;
	}
}

void CPlayer::Update(float fElapsedTime){
	//While the game is not over
	if (m_bGameOver == false){

		//Get an instance of the input manager
		CSGD_DirectInput* pDI = CSGD_DirectInput::GetInstance();

		//Moving Down && Not Attacking
		if (pDI->KeyDown(DIK_S) == true && pDI->KeyDown(DIK_SPACE) == false)
		{
			if (PlayerAnimation->IsPlaying() == false)
				ChangeDirection(PLAYER_MOVE_DOWN, PLAYER_ATTACK_DOWN, 0.0f, 200.0f);
		}

		//Moving Up && not attacking
		if (pDI->KeyDown(DIK_W) == true && pDI->KeyDown(DIK_SPACE) == false)
		{
			if (PlayerAnimation->IsPlaying() == false)
				ChangeDirection(PLAYER_MOVE_UP, PLAYER_ATTACK_UP, 0.0f, -200.0f);
		}

		//Moving left and not attacking
		if (pDI->KeyDown(DIK_A) == true && pDI->KeyDown(DIK_SPACE) == false)
		{
			if (PlayerAnimation->IsPlaying() == false)
				ChangeDirection(PLAYER_MOVE_LEFT, PLAYER_ATTACK_LEFT, -200.0f, 0.0f);
		}

		//Moving right and not attacking
		if (pDI->KeyDown(DIK_D) == true && pDI->KeyDown(DIK_SPACE) == false)
		{
			if (PlayerAnimation->IsPlaying() == false)
				ChangeDirection(PLAYER_MOVE_RIGHT, PLAYER_ATTACK_RIGHT, 200.0f, 0.0f);
		}

		//Attacking
		if (pDI->KeyDown(DIK_SPACE) == true)
		{
			if (PlayerAnimation->IsPlaying() == false)
			{
				switch (m_eMovementDirection)
				{
				case PLAYER_MOVE_UP:
					Attack(PLAYER_ATTACK_UP, UP);
					break;

				case PLAYER_MOVE_DOWN:
					Attack(PLAYER_ATTACK_DOWN, DOWN);
					break;

				case PLAYER_MOVE_LEFT:
					Attack(PLAYER_ATTACK_LEFT, LEFT);
					break;

				case PLAYER_MOVE_RIGHT:
					Attack(PLAYER_ATTACK_RIGHT, RIGHT);
					break;
				}
			}
		}

		//Pressed 1 - Switch weapon to bow
		if (pDI->KeyDown(DIK_1) == true){
			SwitchWeapons(BOW);
		}
		//Pressed 2 - Switched weapon to sword
		if (pDI->KeyDown(DIK_2) == true){
			SwitchWeapons(SWORD);
		}

		//Cheat Code - Invincible
		if (pDI->KeyDown(DIK_LCONTROL) == true && pDI->KeyPressed(DIK_I) == true){
			m_bInvincible = !m_bInvincible;
		}

		//////////////////////////////////////////////////
		//Pause Animation and stop movement upon key release
		if (pDI->KeyReleased(DIK_S) == true){
			PlayerAnimation->Pause(true);
			for (int i = 0; i < NUM_ARMOR_PIECES; i++){
				if (m_cArmor & (1 << i)){
					ArmorAnimations[i]->Pause(true);
				}
			}
			SetVelY(0.0f);
		}
		if (pDI->KeyReleased(DIK_W) == true){
			PlayerAnimation->Pause(true);
			for (int i = 0; i < NUM_ARMOR_PIECES; i++){
				if (m_cArmor & (1 << i)){
					ArmorAnimations[i]->Pause(true);
				}
			}
			SetVelY(0.0f);
		}
		if (pDI->KeyReleased(DIK_A) == true){
			PlayerAnimation->Pause(true);
			for (int i = 0; i < NUM_ARMOR_PIECES; i++){
				if (m_cArmor & (1 << i)){
					ArmorAnimations[i]->Pause(true);
				}
			}
			SetVelX(0.0f);
		}
		if (pDI->KeyReleased(DIK_D) == true){
			PlayerAnimation->Pause(true);
			for (int i = 0; i < NUM_ARMOR_PIECES; i++){
				if (m_cArmor & (1 << i)){
					ArmorAnimations[i]->Pause(true);
				}
			}
			SetVelX(0.0f);
		}
		if (pDI->KeyReleased(DIK_SPACE) == true){
			PlayerAnimation->Pause(true);
			PlayerWeaponAnimation->Pause(true);
			for (int i = 0; i < NUM_ARMOR_PIECES; i++){
				if (m_cArmor & (1 << i)){
					ArmorAnimations[i]->Pause(true);
				}
			}
		}
		//////////////////////////////////////////////////

		//Update Timers
		m_fFireTimer += fElapsedTime;

		//Update Base Class
		CEntity::Update(fElapsedTime);

		//Update Animations
		PlayerAnimation->Update(fElapsedTime);
		PlayerWeaponAnimation->Update(fElapsedTime);
		for (int i = 0; i < NUM_ARMOR_PIECES; i++){
			if (m_cArmor & (1 << i)){
				ArmorAnimations[i]->Update(fElapsedTime);
			}
		}

		//Fire Projectile
		if (PlayerWeaponAnimation->IsPlaying() == true && m_fFireTimer >= m_fMaxFireTimer && m_eWeapon == BOW){
			int nCurrCell = PlayerWeaponAnimation->GetCurrentCell();
			if ((nCurrCell == 8 || nCurrCell == 21 || nCurrCell == 34 || nCurrCell == 47) && (PlayerWeaponAnimation->GetTimeWaited() == 0.0f)){
				CCreateProjectileMessage* pCreateMsg = new CCreateProjectileMessage(this, m_eProjectileDirection, NORMAL);
				CSGD_MessageSystem::GetInstance()->SendMsg(pCreateMsg);
				pCreateMsg = nullptr;
				m_fFireTimer = 0.0f;
			}
		}

		//Make sure player stays inside the world
		StayInWorld();

		//Send the asses threat message
		CSGD_EventSystem::GetInstance()->SendEvent("ASSESS_THREAT", nullptr, nullptr, this);
	}
}

void CPlayer::Render(){
	/////////////////////////////////////
	//Render debug information
	if (m_bDebug){
		RECT rCollisionBox = GetRect();
		CSGD_Direct3D::GetInstance()->DrawRect(rCollisionBox, D3DCOLOR_ARGB(127, 255, 255, 0));

		TOSTRINGSTREAM toss;

		toss << "(" << GetPosX() << ", " << GetPosY() << ")\n"
			<< "Health: " << m_sHealth << '\n'
			<< "Armor: " << m_sArmor << '\n';

		CBitmapFont* pFont = CGame::GetInstance()->GetFont();

		pFont->Draw(toss.str().c_str(), (int) (GetPosX() + GetWidth()), (int) (GetPosY()), .34f, D3DCOLOR_ARGB(255, 0, 0, 0));
	}
	/////////////////////////////////////

	//Create the health bar
	RECT rHealth;
	rHealth.left = 0;
	rHealth.top = (7 - (m_sHealth / 14)) * 8;
	rHealth.right = rHealth.left + 28;
	rHealth.bottom = rHealth.top + 8;

	//Draw Health Bar
	CSGD_TextureManager::GetInstance()->Draw(m_nHealthBarImgID, (int) GetPosX(), (int) GetPosY(), 1.0f, 1.0f, &rHealth);

	//Draw The Player
	PlayerAnimation->Render((int) GetPosX(), (int) GetPosY(), 1.0f, D3DCOLOR_ARGB(255, 255, 255, 255));

	//Draw the player weapon
	PlayerWeaponAnimation->Render((int) GetPosX() - 32, (int) GetPosY() - 32, 1.0f, D3DCOLOR_ARGB(255, 255, 255, 255));

	//If any, Draw players armor
	for (int i = 0; i < NUM_ARMOR_PIECES; i++){
		if (m_cArmor & (1 << i))
			ArmorAnimations[i]->Render((int) GetPosX(), (int) GetPosY(), 1.0f, D3DCOLOR_ARGB(255, 255, 255, 255));
	}



	////////////////////////////////
	// Draw Hud Information
	CBitmapFont* pFont = CGame::GetInstance()->GetFont();

	pFont->Draw(_T("Player"), 20, 20, .6f, D3DCOLOR_ARGB(255, 0, 255, 0));

	TOSTRINGSTREAM toss;
	toss << "Armor: " << m_sArmor;
	pFont->Draw(toss.str().c_str(), 20, 40, .4f, D3DCOLOR_ARGB(255, 255, 255, 0));

	toss.str(_T(""));

	toss << "Kills: " << m_sKills;
	pFont->Draw(toss.str().c_str(), 20, 60, .4f, D3DCOLOR_ARGB(255, 255, 255, 0));
	////////////////////////////////

}

void CPlayer::StayInWorld(){

	if (GetPosX() <= 0)
		SetPosX(0);
	if (GetPosX() >= CGame::GetInstance()->GetScreenWidth() - GetWidth())
		SetPosX((float) (CGame::GetInstance()->GetScreenWidth() - GetWidth()));
	if (GetPosY() <= 32)
		SetPosY(32);
	if (GetPosY() >= CGame::GetInstance()->GetScreenHeight() - (GetHeight() + 16))
		SetPosY((float) (CGame::GetInstance()->GetScreenHeight() - (GetHeight() + 16)));
}

void CPlayer::SwitchWeapons(PlayerWeapon eWeapon){
	switch (eWeapon){
	case SWORD:

		//if you have picked up the weapon
		if (weapons & (1 << Weapon::SWORD)){
			PlayerWeaponAnimation->SetResource(_T("resource/images/Player/Weapons/bb_sabre_sheet_resize.png"));
			m_eWeapon = SWORD;
			m_eCurrentWeaponEndCell = SWORD_CELLS;
		}
		break;
	case BOW:
		//if you have picked up the weapon
		if (weapons & (1 << Weapon::BOW)){
			PlayerWeaponAnimation->SetResource(_T("resource/images/Player/Weapons/bb_recurvebow_sheet(resized).png"));
			m_eWeapon = BOW;
			m_eCurrentWeaponEndCell = BOW_CELLS;
		}
		break;
	}

}

void CPlayer::ChangeDirection(PlayerMovementDirection eDirection, PlayerAttackDirection eAttackDirection, float fVelX, float fVelY)
{
	ChangeAttackDirection(eAttackDirection);

	PlayerWeaponAnimation->SetAnimation(eAttackDirection * m_eCurrentWeaponEndCell, m_eCurrentWeaponEndCell, m_eCurrentWeaponEndCell, true);

	PlayerAnimation->SetAnimation(eDirection, NUMCOLS, 9, true);
	PlayerAnimation->Play(true, m_fPlaySpeed);

	for (int i = 0; i < NUM_ARMOR_PIECES; i++){
		if (m_cArmor & (1 << i)){
			ArmorAnimations[i]->SetAnimation(eDirection, NUMCOLS, 9, true);
			ArmorAnimations[i]->Play(true, m_fPlaySpeed);
		}
	}

	m_eMovementDirection = eDirection;

	SetVelX(fVelX);
	SetVelY(fVelY);
}

void CPlayer::ChangeAttackDirection(PlayerAttackDirection eAttackDirection){
	/*int nBowCells = 13;
	int nSpearCells = 8;
	int nSwordCells = 6;*/
	switch (m_eWeapon){
	case SWORD:
		m_eCurrentWeaponEndCell = SWORD_CELLS;
		break;
	case BOW:
		m_eCurrentWeaponEndCell = BOW_CELLS;
		break;
	}
}

void CPlayer::HandleCollision(const IEntity* pOther) {

	switch (pOther->GetType()){

	case ENT_ENEMY:
		{
			CEnemy* pEnemy = (CEnemy*) (pOther);

			if (PlayerWeaponAnimation->IsPlaying() == true && m_eWeapon == SWORD && m_fFireTimer >= m_fMaxFireTimer){
				int nCurrCell = PlayerWeaponAnimation->GetCurrentCell();
				if (nCurrCell == 4 || nCurrCell == 10 || nCurrCell == 16 || nCurrCell == 22){
					pEnemy->TakeDamage((short) m_fDamageMultiplier);
					CSGD_XAudio2::GetInstance()->SFXPlaySound(SoundManager::GetInstance()->GetSoundID(ENEMY_HIT));
					m_fFireTimer = 0;
				}
			}
		}
		break;

	case ENT_MAGIC_PROJECTILE:
		{
			CMagicProjectile* pProjectile = (CMagicProjectile*) (pOther);

			if (!m_bInvincible && pProjectile->GetOwner() != this){
				m_sHealth -= (short) (pProjectile->GetDamage() - m_sArmor);
				if (m_sHealth > 100)
					m_sHealth = 100;
				CSGD_XAudio2::GetInstance()->SFXPlaySound(SoundManager::GetInstance()->GetSoundID(PLAYER_HIT));
			}

		}
		break;
	case ENT_PROJECTILE:
		{
			CProjectile* projectile = (CProjectile*) (pOther);
			if (!m_bInvincible && (projectile->GetOwner() != this)){
				m_sHealth -= (short) (projectile->GetDamage() - m_sArmor);
				if (m_sHealth > 100)
					m_sHealth = 100;
				CSGD_XAudio2::GetInstance()->SFXPlaySound(SoundManager::GetInstance()->GetSoundID(PLAYER_HIT));
			}

		}
		break;


	case ENT_ARMOR:
		{
			CArmor* pArmor = (CArmor*) (pOther);

			if ((m_cArmor & (1 << pArmor->GetArmorType())) == false){
				m_cArmor |= (1 << pArmor->GetArmorType());
				m_sArmor += pArmor->GetArmorValue();
			}

			CSGD_XAudio2::GetInstance()->SFXPlaySound(SoundManager::GetInstance()->GetSoundID(SWORD_SHEATH));
			CDestroyEntityMessage* pMsg = new CDestroyEntityMessage((CEntity*) (pOther));
			CSGD_MessageSystem::GetInstance()->SendMsg(pMsg);
		}

		break;

	case ENT_WEAPON:
		{
			Weapon* weapon = (Weapon*) (pOther);
			//use bitwise operations to add to inventory
			if ((weapons & (1 << weapon->GetWeaponType())) == false){
				weapons |= (1 << weapon->GetWeaponType());
			}
			CSGD_XAudio2::GetInstance()->SFXPlaySound(SoundManager::GetInstance()->GetSoundID(SWORD_SHEATH));
			CDestroyEntityMessage* destroy_msg = new CDestroyEntityMessage((CEntity*) (pOther));
			CSGD_MessageSystem::GetInstance()->SendMsg(destroy_msg);
		}
		break;
	}
}

void CPlayer::HandleEvent(CEvent* pEvent){

	if (pEvent->GetEventID() == "ENEMY_KILLED"){
		if (pEvent->GetDestination() == this){
			UpdateKills();
		}
	}
	else if (pEvent->GetEventID() == "DEBUG"){
		m_bDebug = !m_bDebug;
	}
}

void CPlayer::Attack(PlayerAttackDirection eAttackDirection, Projectile_Directions eProjectileDirection){

	PlayerAnimation->SetAnimation((eAttackDirection + m_eWeapon) * NUMCOLS, NUMCOLS, m_eCurrentWeaponEndCell, true);
	PlayerWeaponAnimation->SetAnimation(eAttackDirection * m_eCurrentWeaponEndCell, m_eCurrentWeaponEndCell, m_eCurrentWeaponEndCell, true);

	for (int i = 0; i < NUM_ARMOR_PIECES; i++){
		if (m_cArmor & (1 << i)){
			ArmorAnimations[i]->SetAnimation((eAttackDirection + m_eWeapon) * NUMCOLS, NUMCOLS, m_eCurrentWeaponEndCell, true);
			ArmorAnimations[i]->Play(true, m_fPlaySpeed);
		}
	}

	PlayerWeaponAnimation->Play(true, m_fPlaySpeed);
	PlayerAnimation->Play(true, m_fPlaySpeed);

	m_eProjectileDirection = eProjectileDirection;



}

void CPlayer::SetFireTimer(float fFireTimer)
{
	m_fMaxFireTimer /= fFireTimer;
	m_fPlaySpeed = fFireTimer;
}

void CPlayer::SetArmor(int nArmorPiece)
{
	m_cArmor |= (1 << nArmorPiece);

	switch (nArmorPiece)
	{
	case HEAD:
		m_sArmor += HEAD_VALUE;
		break;
	case HANDS:
		m_sArmor += HANDS_VALUE;
		break;
	case FEET:
		m_sArmor += FEET_VALUE;
		break;
	case LEGS:
		m_sArmor += LEGS_VALUE;
		break;
	case SHOULDER:
		m_sArmor += SHOULDER_VALUE;
		break;
	case CHEST:
		m_sArmor += CHEST_VALUE;
		break;
	}

}

void CPlayer::SetupArmor(){
	ArmorAnimations[0] = new CCellAnimation();
	ArmorAnimations[0]->Initialize(64, 64, NUMCOLS, PLAYER_MOVE_DOWN, 9, false, _T("resource/images/Player/Golden Armor/Male/Resized/bb_lpcmalegoldenboots.png"));

	ArmorAnimations[1] = new CCellAnimation();
	ArmorAnimations[1]->Initialize(64, 64, NUMCOLS, PLAYER_MOVE_DOWN, 9, false, _T("resource/images/Player/Golden Armor/Male/Resized/bb_lpcmalegoldengloves.png"));

	ArmorAnimations[2] = new CCellAnimation();
	ArmorAnimations[2]->Initialize(64, 64, NUMCOLS, PLAYER_MOVE_DOWN, 9, false, _T("resource/images/Player/Golden Armor/Male/Resized/bb_lpcmalegoldengreaves.png"));

	ArmorAnimations[3] = new CCellAnimation();
	ArmorAnimations[3]->Initialize(64, 64, NUMCOLS, PLAYER_MOVE_DOWN, 9, false, _T("resource/images/Player/Golden Armor/Male/Resized/bb_lpcmalegoldenhelm.png"));

	ArmorAnimations[4] = new CCellAnimation();
	ArmorAnimations[4]->Initialize(64, 64, NUMCOLS, PLAYER_MOVE_DOWN, 9, false, _T("resource/images/Player/Golden Armor/Male/Resized/bb_lpcmalegoldenplate.png"));

	ArmorAnimations[5] = new CCellAnimation();
	ArmorAnimations[5]->Initialize(64, 64, NUMCOLS, PLAYER_MOVE_DOWN, 9, false, _T("resource/images/Player/Golden Armor/Male/Resized/bb_lpcmalegoldenspaulders.png"));
}







